﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Common.DataAnnotations
{
    /// <summary>
    /// 与另一个参数进行比较
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
    public class CompareToAttribute : ValidationAttribute
    {
        /// <summary>
        /// 比较对象名称
        /// </summary>
        public string TargetField { get; set; }
        /// <summary>
        /// 比较对象展示名称
        /// </summary>
        public string TargetDisplayName { get; set; }
        /// <summary>
        /// 比较选项
        /// </summary>
        public CompareOptions CompareOption { get; set; }
        /// <summary>
        /// 与另一个参数进行比较
        /// </summary>
        /// <param name="targetField"></param>
        /// <param name="option"></param>
        public CompareToAttribute(string targetField, CompareOptions option)
        {
            this.TargetField = targetField;
            //this.TargetDisplayName = targetDisplayName;
            this.CompareOption = option;
        }
        /// <summary>
        /// 
        /// </summary>
        public override bool RequiresValidationContext => true;
        /// <summary>
        /// {name} {operate} {TargetDisplayName} = "name must be 小于或等于 targetDisplayName"
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public override string FormatErrorMessage(string name)
        {
            return string.Format(ErrorMessage ?? "{0}必须{1}{2}", name, GetEnumDescription(CompareOption), TargetDisplayName ?? TargetField);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value == null)
            {
                return null;
            }
            var otherPropertyInfo = validationContext.ObjectType.GetRuntimeProperty(TargetField);
            if (otherPropertyInfo == null)
            {
                return new ValidationResult($"{TargetDisplayName ?? TargetField}不存在");
            }
            if (otherPropertyInfo.GetIndexParameters().Any())
            {
                return new ValidationResult($"比较对象不能为类型索引");
            }
            this.TargetDisplayName = GetDisplayNameForProperty(otherPropertyInfo);
            object otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
            var comparableValue = value as IComparable;
            if (otherPropertyValue == null || comparableValue == null || !(otherPropertyValue is IComparable))
            {
                return null;
            }
            bool result = false;
            switch (this.CompareOption)
            {
                case CompareOptions.LessThan:
                    result = comparableValue.CompareTo(otherPropertyValue) == -1;
                    break;
                case CompareOptions.LessThanOrEquals:
                    result = comparableValue.CompareTo(otherPropertyValue) <= 0;
                    break;
                case CompareOptions.GreatThan:
                    result = comparableValue.CompareTo(otherPropertyValue) == 1;
                    break;
                case CompareOptions.GreatThanOrEquals:
                    result = comparableValue.CompareTo(otherPropertyValue) >= 0;
                    break;
                case CompareOptions.Equals:
                    result = comparableValue.CompareTo(otherPropertyValue) == 0;
                    break;
            }
            if (!result)
            {
                if (string.IsNullOrEmpty(TargetDisplayName))
                {
                    TargetDisplayName = GetDisplayNameForProperty(otherPropertyInfo);
                }
                string[] memberNames = validationContext.MemberName != null ? new[] { validationContext.MemberName } : null;
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), memberNames);
            }
            return null;
        }
        private static string GetDisplayNameForProperty(PropertyInfo property)
        {
            var allAttributes = property.GetCustomAttributes(true);
            var display = allAttributes.OfType<DisplayAttribute>().FirstOrDefault();
            var desc = allAttributes.OfType<DescriptionAttribute>().FirstOrDefault();
            return display?.GetName() ?? desc?.Description ?? property.Name;
        }
        private static string GetEnumDescription(Enum en)
        {
            Type enumType = en.GetType();
            var enumItems = Enum.GetValues(enumType);
            int currVal = Convert.ToInt32(en);
            string currName = Enum.GetName(enumType, currVal);
            var fields = enumType.GetFields();
            var f = fields.FirstOrDefault(x => x.Name == currName);
            var allAttributes = f.GetCustomAttributes(true);
            var desc = allAttributes.OfType<DescriptionAttribute>().FirstOrDefault();
            var display = allAttributes.OfType<DisplayAttribute>().FirstOrDefault();
            return desc?.Description ?? display?.Name ?? currName;
        }
        /// <summary>
        /// 
        /// </summary>
        public enum CompareOptions
        {
            /// <summary>
            /// 小于
            /// </summary>
            [Description("小于")]
            LessThan,

            /// <summary>
            /// 小于或等于
            /// </summary>
            [Description("小于或等于")]
            LessThanOrEquals,

            /// <summary>
            /// 大于
            /// </summary>
            [Description("大于")]
            GreatThan,

            /// <summary>
            /// 大于或等于
            /// </summary>
            [Description("大于或等于")]
            GreatThanOrEquals,

            /// <summary>
            /// 等于
            /// </summary>
            [Description("等于")]
            Equals
        }
    }
}
